Block Grid access settings from parent layout blo...
# help-with-umbraco
b
When following the sample blocks from this example project https://github.com/umbraco/Umbraco.BlockGrid.Example.Website e.g. with "One Column" and "Two Column" blocks with a setting block and the inserting e.g. an Image block into either of these, it there any way to access the parent settings or inherit setting from the layout block? E.g. inside partial view for Image block we can access settings on the block itself, but it doesn't seem we can access the layout block settings?
d
Do any settings exist on the layout blocks?
b
@Dean Leigh Yes, the layout blocks has a shared Section settings block. So I would like to inherit/fallback to some settings from the layout blocks, but data rendered in an Image block, e.g. as
data-*
attributes.
d
You should be able to grab it using
Model.Settings.Value
in any page using
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<Umbraco.Cms.Core.Models.Blocks.BlockGridItem>
b
Yes, but in the Image block it only has access to it own context, the settings on the Image block.. not the "parent" layout blocks.
d
I see what you mean. I am sure you can still get the settings on the parent block from within the child block, I'll try and recreate.
m
you can circument the default view.. and pass a viewDatadictionary down from the layout.. we are doing it for settings on areas..
Copy code
csharp
<div class="umb-block-grid__area-container" style="--umb-block-grid--area-grid-columns: @(Model.AreaGridColumns?.ToString() ?? Model.GridColumns?.ToString() ?? "12");">
      @foreach (var area in Model.Areas)
    {
        @* @await Html.GetBlockGridItemAreaHtmlAsync(area) *@        
        <partial name="blockgrid/area" model="area" view-data='Html.BlocGridSettings(Model.Settings, area.Alias)' />
    }
</div>
simplified
Copy code
csharp
        public static ViewDataDictionary BlocGridSettings(this IHtmlHelper htmlHelper, IPublishedElement? element, string alias)
        {
            object? bgImage = htmlHelper.BlockGridSettingsBgImage(element, alias);

            return new ViewDataDictionary(htmlHelper.ViewContext.ViewData) {{ "bgImage", bgImage } };
        }

        public static object? BlockGridSettingsBgImage(this IHtmlHelper htmlHelper, IPublishedElement? element, string alias)
        {
            return element?.Value($"{alias}BackgroundImage");
        }
The extension methods don't seem to do any more than set default paths to the views.. and remove the vdd param option for partials.
Copy code
public static async Task<IHtmlContent> GetBlockGridItemsHtmlAsync(this IHtmlHelper html, IEnumerable<BlockGridItem> items, string template = DefaultItemsTemplate)
        => await html.PartialAsync(DefaultFolderTemplate(template), items);

    public static async Task<IHtmlContent> GetBlockGridItemAreasHtmlAsync(this IHtmlHelper html, BlockGridItem item, string template = DefaultItemAreasTemplate)
        => await html.PartialAsync(DefaultFolderTemplate(template), item);

    public static async Task<IHtmlContent> GetBlockGridItemAreaHtmlAsync(this IHtmlHelper html, BlockGridArea area, string template = DefaultItemAreaTemplate)
        => await html.PartialAsync(DefaultFolderTemplate(template), area);
perhaps core could be extended...
Copy code
public static async Task<IHtmlContent> GetBlockGridItemAreasHtmlAsync(this IHtmlHelper html, BlockGridItem item, string template = DefaultItemAreasTemplate, ViewDataDictionary vdd)
        => await html.PartialAsync(DefaultFolderTemplate(template), item, vdd);
b
Yes, I tried something like this:
Copy code
public static async Task<IHtmlContent> GetBlockGridItemAreaHtmlAsync(this IHtmlHelper html, BlockGridArea area, string template = "area", ViewDataDictionary? viewData = null)
            => await html.PartialAsync($"blockgrid/{template}", area, viewData);
and then replacing this in core `areas.cshtml`:
Copy code
<div class="umb-block-grid__area-container"
     style="--umb-block-grid--area-grid-columns: @(Model.AreaGridColumns?.ToString() ?? Model.GridColumns?.ToString() ?? "12");">
    @foreach (var area in Model.Areas)
    {
        //@await Html.GetBlockGridItemAreaHtmlAsync(area)
        @await Html.GetBlockGridItemAreaHtmlAsync(area, viewData: new ViewDataDictionary(ViewData) { { "settingsSection", Model.Settings as BlockSettingsSection }})
    }
</div>
but it seems I get this error, because we have multiple layout sections with the settings block.
d
It is a shame areas are not treated as blocks with settings in their own right tbh fwiw :I know we can hack around this and I can also appreciate why they were not
I have been getting settings from layout blocks and using them in partials and even _Layout files Here I am getting the label of a picked colour set on a layout block:
Copy code
@{
    if (Model?.Areas.Any() != true) { return; }

    bool hasSettings = Model.Settings != null;
    string colorLabel = null;

    if (hasSettings)
    {
        var backgroundColour = Model.Settings.Value<ColorPickerValueConverter.PickedColor>("layoutSettingsColourPicker");
        colorLabel = backgroundColour?.Label;
    }
}

<div class="layout py-3" style="@(hasSettings && colorLabel != null ? $"background-color: var({colorLabel});" : "")" data-block-alias="@Model.Content.ContentType.Alias">
    @RenderBody()
</div>
b
In areas I can also access settings, but I need to pass it on to the block itself. The key need to include e.g. area alias to avoid duplicate keys and I have two extension methods:
Copy code
public static async Task<IHtmlContent> GetBlockGridItemAreaHtmlAsync(this IHtmlHelper html, BlockGridArea area, string template = "area", ViewDataDictionary? viewData = null)
            => await html.PartialAsync($"blockgrid/{template}", area, viewData);

        public static async Task<IHtmlContent> GetBlockGridItemsHtmlAsync(this IHtmlHelper html, IEnumerable<BlockGridItem> items, string template = "items", ViewDataDictionary? viewData = null)
            => await html.PartialAsync($"blockgrid/{template}", items, viewData);
In default.cshtml:
Copy code
<div class="umb-block-grid"
     data-grid-columns="@(Model.GridColumns?.ToString() ?? "12");"
     style="--umb-block-grid--grid-columns: @(Model.GridColumns?.ToString() ?? "12");">
    @await Html.GetBlockGridItemsHtmlAsync(Model, viewData: new ViewDataDictionary(ViewData))
</div>
In items.cshtml:
Copy code
@await Html.PartialAsync(partialViewName, item, new ViewDataDictionary(ViewData))
In areas.cshtml
Copy code
<div class="umb-block-grid__area-container"
     style="--umb-block-grid--area-grid-columns: @(Model.AreaGridColumns?.ToString() ?? Model.GridColumns?.ToString() ?? "12");">
    @foreach (var area in Model.Areas)
    {
        //@await Html.GetBlockGridItemAreaHtmlAsync(area)
        @await Html.GetBlockGridItemAreaHtmlAsync(area, viewData: new ViewDataDictionary(ViewData) { { $"{area.Alias}_settingsSection", Model.Settings as BlockSettingsSection }})
    }
</div>
and in area.cshtml
Copy code
<div class="umb-block-grid__area"
     data-area-col-span="@Model.ColumnSpan"
     data-area-row-span="@Model.RowSpan"
     data-area-alias="@Model.Alias"
     style="--umb-block-grid--grid-columns: @Model.ColumnSpan;--umb-block-grid--area-column-span: @Model.ColumnSpan; --umb-block-grid--area-row-span: @Model.RowSpan;">
    @await Html.GetBlockGridItemsHtmlAsync(Model, viewData: new ViewDataDictionary(ViewData))
</div>
In a block I can then access the settings, where Image block is placed in "left" area.
Copy code
var settingsSection = ViewData[$"left_settingsSection"] != null ? ViewData[$"left_settingsSection"] as BlockSettingsSection : null;
m
Is that because you are adding the new viewdata entry to the existing page viewdata, and if you have it chaining down then would give conflicts? Easiest way I guess.. but can you not have unique viewdata keys? or maybe you have to look to create a new ViewData?
b
It works with the steps here: https://github.com/umbraco/Umbraco-CMS/pull/14557 ... but I think Umbraco core extension methods should allow passing in view data, which is passed to partial (or view component).
m
I also prefer the tag helper as it works out async vs sync for you and also doesn't scare the frontened to death. 🙂
along with umbtaghelpers.. makes for nice clean views.
Also it unfortunately means that blockpreviews package can't be used, for the same reason no passing viewData
d
You just answered a question I was thinking of asking - should I change the partial includes that come with block list to tag helpers ? So it's a no 🙂
m
did I?
d
Yeah I am using the Block Preview Package
m
it's not that the
<partial />
doesn't work.. it's that I've added passing in
view-data
which then means that block preview package doesn't pass on the
view-model
so trying to render the partial fails.
d
I'm also using view-data
m
🙂
d
I have picked up habits from playing with Razor pages and can't remember what comes from where anymore
m
might be as easy as updating the block preview inlcudes too?
d
I am assuming a lot of the new syntax (and helpers) will make their way into the new back office
m
think it's one of those marmite things..
<partial />
vs
Html.PartialAsync()
22 Views